﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using ShkSimulation.carsharing.impl.elements;

namespace ShkSimulation.desktop.component {
	
	public partial class RoadControl : UserControl {
		
		private Color roadColor;
		private Color defaultColor = Color.DimGray;
		private Color centerLineColor = Color.WhiteSmoke;
		private int gradientWidth = 50;
		private Point end;
		private Point start;
		private int width;
		private int stationWidth;
		private int scan = 3;
		private double segment = 9;
		private Road road;
		private ToolTip toolTip;

		public int GradientWidth {
			get { return gradientWidth; }
			set {
				gradientWidth = value;
				Refresh();
			}
		}

		public Point End {
			get { return end; }
			set {
				end = value;
				this.Size = new Size(Math.Max(start.X, end.X) + width, Math.Max(start.Y, end.Y) + width);
				Refresh();
			}
		}

		public Point Start {
			get { return start; }
			set {
				start = value;
				this.Size = new Size(Math.Max(start.X, end.X) + width, Math.Max(start.Y, end.Y) + width);
				Refresh();
			}
		}

		[Description("颜色"), Category("自定义")]
		public Color RoadColor {
			get { return roadColor; }
			set {
				roadColor = value;
				Refresh();
			}
		}

		[Description("线条颜色"), Category("自定义")]
		public Color CenterLineColor {
			get { return centerLineColor; }
			set {
				centerLineColor = value;
				Refresh();
			}
		}

		public RoadControl(Road road, Point start, Point end, int width) {
			this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
			this.SetStyle(ControlStyles.DoubleBuffer, true);
			this.SetStyle(ControlStyles.ResizeRedraw, true);
			this.SetStyle(ControlStyles.Selectable, true);
			this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			this.SetStyle(ControlStyles.UserPaint, true);
			this.AutoScaleMode = AutoScaleMode.Inherit;
			toolTip = new ToolTip();
			toolTip.AutoPopDelay = 100000;
			toolTip.InitialDelay = 50;
			toolTip.AutomaticDelay = 100000;
			toolTip.ReshowDelay = 50;
			toolTip.ShowAlways = true;
			this.MouseHover += OnMouseHover;
			this.Size = new Size(Math.Max(start.X, end.X) + width, Math.Max(start.Y, end.Y) + width);
			this.width =  width / 4;
			this.stationWidth = width;
			this.start = start;
			this.end = end;
			this.road = road;
			roadColor = defaultColor;
		}
				
		public void ResetColor() {
			RoadColor = defaultColor;
		}
		
		private void OnMouseHover(object sender, EventArgs e) {
			toolTip.SetToolTip(this, road.ToString());
		}

		protected override void OnPaint(PaintEventArgs e) {
			base.OnPaint(e);
			Graphics graphics = e.Graphics;
			graphics.SmoothingMode = SmoothingMode.AntiAlias;
			GraphicsPath roadPath = new GraphicsPath();
			PointF[] fourPoint = this.road.LastStationId < this.road.NextStationId? GetRectangleLastToNext(): GetRectangleNextToLast();
			roadPath.AddLines(fourPoint);
			roadPath.CloseAllFigures();
			Region = new Region(roadPath);
			graphics.FillPath(new SolidBrush(roadColor), roadPath);
			DrawCenterLine(graphics);
		}

		private PointF[] GetRectangleLastToNext() {
			PointF[] result = new PointF[4];
			double angle;
			if (end.X == start.X) {
				angle = Math.PI / scan;
			} else {
				angle = Math.Atan((end.Y - start.Y + 0.0) / (end.X - start.X));
			}
			int oneWayWidth = Convert.ToInt32(stationWidth / segment);
			int xMove = Convert.ToInt32(Math.Abs(Math.Sin(angle)) * oneWayWidth);
			int yMove = Convert.ToInt32(Math.Abs(Math.Cos(angle)) * oneWayWidth);
			if ((end.X <= start.X && end.Y <= start.Y) || (end.X >= start.X && end.Y >= start.Y)) {
				result[0] = new PointF(start.X + xMove, start.Y - yMove);
				result[1] = new PointF(start.X + xMove * scan, start.Y - yMove * scan);
				result[2] = new PointF(end.X + xMove * scan, end.Y - yMove * scan);
				result[3] = new PointF(end.X + xMove, end.Y - yMove);
			} else {
				result[0] = new PointF(start.X + xMove, start.Y + yMove);
				result[1] = new PointF(start.X + xMove * scan, start.Y + yMove * scan);
				result[2] = new PointF(end.X + xMove * scan, end.Y + yMove * scan);
				result[3] = new PointF(end.X + xMove, end.Y + yMove);
			}
			return result;
		}
		
		private PointF[] GetRectangleNextToLast() {
			PointF[] result = new[] {
				new PointF(0, 0),
				new PointF(this.ClientRectangle.Right, 0),
				new PointF(this.ClientRectangle.Right, this.Height),
				new PointF(0, this.Height)
			};
			double angle;
			if (end.X == start.X) {
				angle = Math.PI / 2;
			} else {
				angle = Math.Atan((end.Y - start.Y + 0.0) / (end.X - start.X));
			}
			int oneWayWidth = Convert.ToInt32(stationWidth / segment);
			int xMove = Convert.ToInt32(Math.Abs(Math.Sin(angle)) * oneWayWidth);
			int yMove = Convert.ToInt32(Math.Abs(Math.Cos(angle)) * oneWayWidth);
			if ((end.X <= start.X && end.Y <= start.Y) || (end.X >= start.X && end.Y >= start.Y)) {
				result[0] = new PointF(start.X - xMove, start.Y + yMove);
				result[1] = new PointF(start.X - xMove * scan, start.Y + yMove * scan);
				result[2] = new PointF(end.X - xMove * scan, end.Y + yMove * scan);
				result[3] = new PointF(end.X - xMove, end.Y + yMove);
			} else {
				result[0] = new PointF(start.X - xMove, start.Y - yMove);
				result[1] = new PointF(start.X - xMove * scan, start.Y - yMove * scan);
				result[2] = new PointF(end.X - xMove * scan, end.Y - yMove * scan);
				result[3] = new PointF(end.X - xMove, end.Y - yMove);
			}
			return result;
		}

		private void DrawCenterLine(Graphics graphics) {
			int[] xy = GetCenterLinePoints();
			int oneWayWidth = Convert.ToInt32(stationWidth / segment);
			GraphicsPath centerLine = new GraphicsPath();
			centerLine.AddLine(xy[0], xy[1], xy[2], xy[3]);
			DrawGradient(graphics, centerLine, GradientWidth);
			int centerLineWidth = Convert.ToInt32(oneWayWidth * (scan - 1) / 4);
			Pen pen = new Pen(new SolidBrush(centerLineColor), centerLineWidth);
			pen.DashPattern = new float[] {6, 6};
			graphics.DrawPath(pen, centerLine);
		}
		
		private int[] GetCenterLinePoints() {
			double angle;
			if (end.X == start.X) {
				angle = Math.PI / 2;
			} else {
				angle = Math.Atan((end.Y - start.Y + 0.0) / (end.X - start.X));
			}
			int oneWayWidth = Convert.ToInt32(stationWidth / segment);
			int xMove = Convert.ToInt32(Math.Abs(Math.Sin(angle)) * oneWayWidth * (1 + scan) / 2.0);
			int yMove = Convert.ToInt32(Math.Abs(Math.Cos(angle)) * oneWayWidth * (1 + scan) / 2.0);
			if (road.LastStationId < road.NextStationId) {
				if ((end.X <= start.X && end.Y <= start.Y) || (end.X >= start.X && end.Y >= start.Y)) {
					return new[]{start.X + xMove, start.Y - yMove, end.X + xMove, end.Y - yMove};
				} else {
					return new[]{start.X + xMove, start.Y + yMove, end.X + xMove, end.Y + yMove};
				}
			} else {
				if ((end.X <= start.X && end.Y <= start.Y) || (end.X >= start.X && end.Y >= start.Y)) {
					return new[]{start.X - xMove, start.Y + yMove, end.X - xMove, end.Y + yMove};
				} else {
					return new[]{start.X - xMove, start.Y - yMove, end.X - xMove, end.Y - yMove};
				}
			}
		}
		
		private void DrawGradient(Graphics graphics, GraphicsPath linePath, int gradientWidth1) {
			int gradientCount = gradientWidth1 / 2 / 4;
			for (int i = 0; i < gradientCount; i++) {
				int penWidth = gradientWidth1 / 2 - 4 * i;
				if (penWidth <= 0)
					penWidth = 1;
				graphics.DrawPath(
					new Pen(new SolidBrush(Color.FromArgb(40, Color.White.R, Color.White.G, Color.White.B)), penWidth),
					linePath);
				if (penWidth == 1) {
					break;
				}
			}
		}

		private PointF[] GetOneWayRectangle() {
			PointF[] result = new[] {
				new PointF(0, 0),
				new PointF(this.ClientRectangle.Right, 0),
				new PointF(this.ClientRectangle.Right, this.Height),
				new PointF(0, this.Height)
			};
			double angle;
			if (end.X == start.X) {
				angle = Math.PI / 2;
			} else {
				angle = Math.Atan((end.Y - start.Y + 0.0) / (end.X - start.X));
			}
			int xMove = Convert.ToInt32(Math.Abs(Math.Sin(angle)) * width / 2);
			int yMove = Convert.ToInt32(Math.Abs(Math.Cos(angle)) * width / 2);
			if ((end.X < start.X && end.Y < start.Y) || (end.X > start.X && end.Y > start.Y)) {
				result[0] = new PointF(start.X + xMove, start.Y - yMove);
				result[1] = new PointF(start.X - xMove, start.Y + yMove);
				result[2] = new PointF(end.X - xMove, end.Y + yMove);
				result[3] = new PointF(end.X + xMove, end.Y - yMove);
			} else {
				result[0] = new PointF(start.X + xMove, start.Y + yMove);
				result[1] = new PointF(start.X - xMove, start.Y - yMove);
				result[2] = new PointF(end.X - xMove, end.Y - yMove);
				result[3] = new PointF(end.X + xMove, end.Y + yMove);
			}
			return result;
		}
		
		private void DrawOneWayCenterLine(Graphics graphics) {
			GraphicsPath centerLine = new GraphicsPath();
			centerLine.AddLine(start.X, start.Y, end.X, end.Y);
			DrawGradient(graphics, centerLine, GradientWidth);
			Pen pen = new Pen(new SolidBrush(centerLineColor), 6);
			pen.DashPattern = new float[] {6, 6};
			graphics.DrawPath(pen, centerLine);
		}

	}

}